# superclass of projects
class Project < ActiveRecord::Base
  # note: :include => :status here and the named_scopes in the Issue model cause SQL badness.
  has_many :issues, :include => [:priority, :submitter]

  has_many :memberships
  has_many :members, :through => :memberships, :source => :user
  
  validates_presence_of :displayname, :on => :create, :message => "can't be blank"
  validates_uniqueness_of :displayname, :on => :create, :message => "must be unique"

  validates_presence_of :name, :on => :create, :message => "can't be blank"
  validates_uniqueness_of :name, :on => :create, :message => "must be unique"
  
  def validate
    name =~ /[0-9a-zA-Z$-_.+!*'\(\)]*/
    unless name == $~[0]
      errors.add_to_base "name must contain only URL-safe characters"
    end
  end
  
  def member?(user)
    true
  end

  # returns the correct project by name or id
  def self.find_by_name_or_id( name_or_id )
    # catch the simple case
    return find( name_or_id ) if name_or_id.is_a? Numeric

    if name_or_id =~ /^\d+$/
      # string numeric, look it up with a simple find
      find( name_or_id.to_i )
    else
      # not numeric - must be a name
      find( :first, :conditions => { :name => name_or_id } )
    end
  end

  # returns project names from the db
  #
  # if +match+ is given, only returns records that match the search string
  # 
  # if <tt>options[:after]</tt> then all returned strings must begin with match
  def self.names( match = nil, options = {} )
    if match
      find( :all, :select => 'name', :conditions =>
          ["name LIKE ?", ( options[:after] ? '' : '%' ) + match + '%' ]
        ).map( &:name )
    else
      find( :all, :select => 'name' ).map( &:name )
    end
  end
  
  #returns the number of closed issues in this project
  def closed_issues_count
    Issue.find_closed_for(self).length
  end

  #returns the number of open issues in this project
  def open_issues_count
    Issue.find_open_for(self).length
  end

  def inspect
    "Project (#{self})"
  end
  
  def to_s
    if displayname
      displayname
    else
      name
    end
  end
end

# remote project concrete class
#
# this represents projects that are hosted by other services and can be
# accessed through ActiveResource objects
class RemoteProject < Project
  validates_presence_of :remote_key

  def inspect
    "RemoteProject( #{remote_key}, #{issues.size} issues, #{users.size} members )"
  end
end

# local project concrete class
#
# this represents projects that are not tied to remote projects, their
# information is stored in the FlawMill database
class LocalProject < Project
  # returns whether the project has user as a member
  # also returns true if the project is open
  def member?( user )
    return true if open?
    Membership.member?( self, user )
  end

  def inspect
    "LocalProject( #{name}, #{issues.size} issues, #{members.size} members )"
  end
end
